-
Notifications
You must be signed in to change notification settings - Fork 188
Unity client sdk #1105
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Unity client sdk #1105
Conversation
Moved Unity SDK template files from 'templates/unity/Runtime' and 'templates/unity/Editor' to 'templates/unity/Assets/Runtime' and 'templates/unity/Assets/Editor' for better alignment with Unity project conventions. Updated getFiles() in Unity.php to reflect new paths and added support for copying plugin DLLs and project settings. Improved file upload logic in Client.cs.twig to handle streams and byte arrays more robustly, and removed Unity-specific logging from Exception.cs.twig. Minor fixes in Realtime.cs.twig and Role.cs.twig for namespace and async handling.
Introduces Unity2021 test support by adding a Unity2021Test.php, Unity test source files, and updating the GitHub Actions workflow to include Unity2021 in the test matrix and set the UNITY_LICENSE environment variable. This enables automated testing for the Unity SDK within the CI pipeline.
I am also attaching instructions on how to obtain a Unity license painlessly. |
The basic functionality tested in the test is guaranteed to work, but I'm not sure about the rest. I will continue to refine it. Also, a question: What to implement next for the client SDK? |
The test failed because the secret is not configured in appwrite/sdk-generator. The successful test is here: |
// Cookie support
client.SetCookie() / client.GetCookie()
// Real-time subscriptions
realtime.Subscribe(channels, callback)
// Connection testing
await client.Ping() The successful test is here: |
Introduces OAuth2 authentication flow and deep link handling for Unity (Editor, WebGL, iOS, Android) via a new WebAuthComponent. Adds cookie management with persistent storage and WebGL credentials support. Updates code generation templates to support new authentication, cookie handling, and platform-specific logic. Adds AndroidManifest.xml for OAuth deep link support and WebGLCookies.jslib for WebGL credential patching.
Extended the Unity service template to include 'general' services for extension imports. Removed OAuth2 related test cases and responses from Unity2021Test.php and Tests.cs, reflecting changes in service requirements.
stable Introduces OAuth2 authentication flow and deep link handling for Unity (Editor, WebGL, iOS, Android) via a new WebAuthComponent. Adds cookie management with persistent storage and WebGL credentials support. Updates code generation templates to support new authentication, cookie handling, and platform-specific logic. Adds AndroidManifest.xml for OAuth deep link support and WebGLCookies.jslib for WebGL credential patching.
OAuth functionality has been added and tested on Web and Android platforms. While implementing this feature, I accidentally ended up creating a small Unity Playground, so you can now try out the functionality directly in your browser. Repositories: |
Add tests for new Query methods in Unity
The Unity SDK README was rewritten for clarity, modernized with improved badge links, updated installation and dependency instructions, and new quick start code samples for both AppwriteManager and direct Client usage
cosmetic fix
normalize domain value
The Unity language class now extends DotNet, removing Unity-specific overrides and template files. Unity code generation now uses shared .NET templates, reducing duplication and maintenance overhead. Updated file mappings and removed Unity-specific template files accordingly.
Refactor Unity SDK to inherit from DotNet and reuse templates
In any case, it will be necessary to accept #1138 first as it synchronized Unity and DotNet in general. |
The WebAuthComponent is now only included when the UNI_TASK symbol is defined, in addition to the existing Unity platform checks. This helps control compilation based on the presence of the UniTask library.
Rewrote the setup assistant and window logic to use reliable asynchronous package management with callbacks, improved UI responsiveness, and removed legacy code paths. The assistant now prevents concurrent operations, provides better user feedback, and ensures package installation and status checks are robust and non-blocking. The setup window UI is updated to reflect busy states and uses the new async methods for package installation.
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughIntroduces Unity as a new SDK language provider (class Appwrite\SDK\Language\Unity) and updates the example generator to produce a Unity SDK. Adds extensive Unity templates (editor windows, setup assistant, runtime client/core, realtime, WebAuth, utilities, samples, asmdef/csc, package manifests, ProjectSettings, README, LICENSE, CHANGELOG, docs, plugins, and cookie/WebGL helpers). Adds Unity-specific tests and test assets (Unity2021Test, Tests.cs, Tests.asmdef). Updates CI to expose UNITY_LICENSE from secrets and include Unity2021 in the build matrix. Adds a UNITY_TEST_MODE flag to filter files during tests. Pre-merge checks (3 passed)✅ Passed checks (3 passed)
✨ Finishing touches🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 30
♻️ Duplicate comments (1)
tests/languages/unity/Tests.cs (1)
56-75
: Eliminate flakiness: replace fixed delays with a completion signal + timeoutWaiting a fixed 5s can be flaky and slow. Use a TaskCompletionSource signaled by the callback and race it with a timeout.
- string realtimeResponse = "No realtime message received within timeout"; - RealtimeSubscription subscription = null; - subscription = realtime.Subscribe(new [] { "tests" }, (eventData) => + var tcs = new TaskCompletionSource<string>(); + RealtimeSubscription subscription = null; + subscription = realtime.Subscribe(new [] { "tests" }, (eventData) => { Debug.Log($"[Test] Realtime callback invoked! Payload count: {eventData.Payload?.Count}"); if (eventData.Payload != null && eventData.Payload.TryGetValue("response", out var value)) { Debug.Log($"[Test] Found response value: {value}"); - realtimeResponse = value.ToString(); - Debug.Log($"[Test] Updated realtimeResponse to: {realtimeResponse}"); + tcs.TrySetResult(value.ToString()); } else { Debug.Log("[Test] No 'response' key found in payload"); } subscription?.Close(); }); - - await Task.Delay(5000); + var completed = await Task.WhenAny(tcs.Task, Task.Delay(TimeSpan.FromSeconds(10))); + var realtimeResponse = completed == tcs.Task ? tcs.Task.Result : "No realtime message received within timeout"; @@ - await Task.Delay(5000); - Debug.Log(realtimeResponse); + Debug.Log(realtimeResponse);Also applies to: 185-189
🧹 Nitpick comments (28)
templates/unity/LICENSE.twig (1)
1-1
: License rendering is fine; consider avoiding escaping.If the generator enables auto-escaping for text templates, render raw to preserve formatting.
-{{sdk.license}} +{{ sdk.license|default('')|raw }}templates/unity/ProjectSettings/ProjectVersion.txt (1)
1-2
: Parameterize Unity Editor version to reduce maintenance.Hardcoding the exact patch ties the generator to a single image/tag. Make this a Twig template and feed from config, with sensible defaults.
-m_EditorVersion: 2021.3.45f1 -m_EditorVersionWithRevision: 2021.3.45f1 (3409e2af086f) +m_EditorVersion: {{ unity.editorVersion|default('2021.3.45f1') }} +m_EditorVersionWithRevision: {{ unity.editorVersionWithRevision|default('2021.3.45f1 (3409e2af086f)') }}Also consider targeting a current LTS in CI while still allowing generation for older versions via config.
templates/unity/Assets/Runtime/Core/csc.rsp (1)
1-1
: Confirm csc.rsp pickup scope and add trailing newline.Unity picks up csc.rsp files under Assets, but placement nuances exist across versions. Please verify this file is detected under Assets/Runtime/Core for 2021.3; if not, move a single csc.rsp to Assets/ to apply project-wide. Add a newline at EOF to avoid diff churn.
.github/workflows/tests.yml (1)
53-53
: Matrix entry name implies version-lock; consider generalizing.If you’ll test multiple Unity LTS versions over time, consider “Unity” with a separate matrix dimension for version, or generate from a variable. This reduces churn across files.
templates/unity/Assets/Runtime/Core/Appwrite.Core.asmdef.twig (1)
4-6
: UniTask reference vs optional dependency. Decide one path and align.Referencing “UniTask” here hard-requires the package; the versionDefines block implies it should be optional. Either:
- Keep the reference and declare the dependency in your Unity package (package.json) so consumers get UniTask automatically, or
- Make UniTask truly optional: remove the reference and guard all usages behind
#if UNI_TASK
with no public API surface exposing UniTask types when absent.If choosing optional, apply:
- "references": [ - "UniTask" - ], + "references": [],If choosing required, add UniTask to package.json “dependencies” (see comment in package.json.twig).
templates/unity/Assets/Editor/Appwrite.Editor.asmdef.twig (1)
2-7
: Set rootNamespace to the Editor namespace for consistency.Editor assemblies typically use a distinct namespace to avoid collisions with runtime types.
- "name": "{{ spec.title | caseUcfirst }}.Editor", - "rootNamespace": "{{ spec.title | caseUcfirst }}", + "name": "{{ spec.title | caseUcfirst }}.Editor", + "rootNamespace": "{{ spec.title | caseUcfirst }}.Editor",templates/unity/package.json.twig (2)
2-7
: Package identity should be vendor-agnostic and configurable.Hardcoding
com.fellmonkey
isn’t ideal for the generator. Expose a vendor/namespace input and default to org-owned value.- "name": "com.fellmonkey.{{spec.title | caseLower}}-sdk", + "name": "{{ sdk.packageName|default('com.appwrite.' ~ (spec.title | caseLower) ~ '-sdk') }}", "version": "{{sdk.version}}", "displayName": "{{spec.title}} SDK", "description": "{{spec.description}}", - "unity": "2021.3", + "unity": "{{ sdk.unityMinVersion|default('2021.3') }}",
8-17
: Add runtime UPM dependencies to templates/unity/package.json.twigInsert a "dependencies" block immediately after the "keywords" array in templates/unity/package.json.twig declaring com.cysharp.unitask and com.endel.nativewebsocket (use the same UPM URLs/versions from templates/unity/Packages/manifest.json) so consumers auto-install required packages.
templates/unity/Packages/packages-lock.json (1)
1-490
: Reconsider shipping a lockfile in a generator template.packages-lock.json tightly pins the dependency graph to Unity 2021.3 and specific package states. For a cross-version template, this can cause resolution failures or needless churn. Prefer:
- Omit this file from the template (let UPM resolve from manifest), or
- Generate it conditionally per targeted Unity editor in CI and don’t commit it.
templates/unity/Packages/manifest.json (2)
5-15
: Trim dev-only/editor convenience packages from a library template.Consider removing non-essential deps to reduce install time and conflicts:
- com.unity.collab-proxy
- com.unity.ide.rider / com.unity.ide.visualstudio / com.unity.ide.vscode
- com.unity.feature.2d (and associated 2D extras) unless samples require them
- com.unity.visualscripting
This keeps the SDK lean; samples can declare extras via a Samples~ package or Setup Assistant.
If samples actually require these, keep them but scope to Samples~/sampleProject/manifest.json to avoid polluting consumer projects.
15-46
: Scope core modules to the minimum needed.For an HTTP/WebSocket SDK, builtin modules likely needed are: jsonserialize, unitywebrequest, unitywebrequesttexture, (optionally) imgui/ugui for samples. Others (audio, physics, terrain, etc.) can be omitted.
templates/unity/README.md.twig (2)
6-6
: Swap Travis badge for GitHub Actions.Project CI is on GitHub Actions per PR notes; the Travis badge is misleading.
-[](https://travis-ci.com/appwrite/sdk-generator) +[](https://github.com/appwrite/sdk-generator/actions/workflows/tests.yml)
5-5
: Make the Unity version badge configurable.Hardcoding 2021.3+ is at odds with reviewer feedback to target newer LTS.
- + }}-blue.svg)Set sdk.unityMinVersion in the language provider to align with the chosen LTS (2022.3+ or Unity 6).
tests/languages/unity/Tests.cs (3)
117-121
: Avoid unsafe cast for Redirect() resultGuard the cast to prevent NullReferenceException in case the return type changes.
- var result = await general.Redirect(); - Debug.Log((result as Dictionary<string, object>)["result"]); + var result = await general.Redirect(); + if (result is Dictionary<string, object> dict && dict.TryGetValue("result", out var res)) + Debug.Log(res); + else + Assert.Fail("Unexpected redirect payload.");
255-258
: Prefer Destroy over DestroyImmediate in PlayModeIn PlayMode tests, use Destroy to avoid editor-only semantics.
- Object.DestroyImmediate(realtimeObject); + Object.Destroy(realtimeObject);
30-35
: Surface original async exceptionThrowing task.Exception rethrows AggregateException. Prefer GetAwaiter().GetResult() to preserve original stack for single-fault tasks.
- if (task.Exception != null) - { - Debug.LogError($"Test failed with exception: {task.Exception}"); - throw task.Exception; - } + if (task.IsFaulted) + { + Debug.LogError($"Test failed with exception: {task.Exception}"); + task.GetAwaiter().GetResult(); // throws original + }tests/Unity2021Test.php (1)
20-20
: Fail fast when UNITY_LICENSE is missing; preserve exit codes through pipesCurrently, tests proceed even without a valid license. Add an explicit check and pipefail to avoid false greens.
-protected string $command = 'docker run --network="mockapi" --rm -v "$(pwd):/project" -w /project/tests/sdks/unity -e UNITY_LICENSE unityci/editor:ubuntu-2021.3.45f1-base-3.1.0 /bin/bash -c "echo \"\$UNITY_LICENSE\" > Unity_lic.ulf && /opt/unity/Editor/Unity -nographics -batchmode -manualLicenseFile Unity_lic.ulf -quit || true && /opt/unity/Editor/Unity -projectPath . -batchmode -nographics -runTests -testPlatform PlayMode -stackTraceLogType None -logFile - 2>/dev/null | sed -n \'/Test Started/,\$p\' | grep -v -E \'^(UnityEngine\\.|System\\.|Cysharp\\.|\\(Filename:|\\[.*\\]|##utp:|^\\s*\$|The header Origin is managed automatically|Connected to realtime:)\' | grep -v \'StackTraceUtility\'"'; +protected string $command = 'docker run --network="mockapi" --rm -v "$(pwd):/project" -w /project/tests/sdks/unity -e UNITY_LICENSE unityci/editor:ubuntu-2021.3.45f1-base-3.1.0 /bin/bash -lc "\ +set -euo pipefail; \ +if [ -z \"${UNITY_LICENSE:-}\" ]; then echo \"UNITY_LICENSE secret missing\" >&2; exit 1; fi; \ +printf \"%s\" \"\$UNITY_LICENSE\" > Unity_lic.ulf; \ +/opt/unity/Editor/Unity -nographics -batchmode -manualLicenseFile Unity_lic.ulf -quit || true; \ +/opt/unity/Editor/Unity -projectPath . -batchmode -nographics -runTests -testPlatform PlayMode -stackTraceLogType None -logFile - \ + 2>/dev/null | sed -n \'/Test Started/,\$p\' | grep -v -E \'^(UnityEngine\\.|System\\.|Cysharp\\.|\\(Filename:|\\[.*\\]|##utp:|^\\s*\$|The header Origin is managed automatically|Connected to realtime:)\' | grep -v \'StackTraceUtility\'"';templates/unity/Assets/Runtime/Core/Plugins/WebGLCookies.jslib (1)
7-37
: Consider more granular error handling in EnableWebGLHttpCredentialsWhile the function has a try-catch block, the empty catch could mask legitimate errors that developers should know about during debugging.
Consider logging errors in development builds:
- } catch (e) { /* noop */ } + } catch (e) { + if (typeof console !== 'undefined' && console.error) { + console.error('[Appwrite] Failed to enable WebGL HTTP credentials:', e); + } + }src/SDK/Language/Unity.php (1)
390-405
: Test mode filtering logic could be more maintainableThe test mode filtering uses string matching on template destinations, which could break if template paths change. Consider using a more robust approach.
Consider defining test exclusions by scope or adding a test-specific property to file definitions:
// Alternative approach: Add 'excludeInTest' property to file definitions $files = [ [ 'scope' => 'default', 'destination' => 'Assets/Runtime/Utilities/{{ spec.title | caseUcfirst }}Utilities.cs', 'template' => 'unity/Assets/Runtime/Utilities/AppwriteUtilities.cs.twig', 'excludeInTest' => true, // Add this property ], // ... other files ]; // Then filter more simply: if (isset($GLOBALS['UNITY_TEST_MODE']) && $GLOBALS['UNITY_TEST_MODE'] === true) { $files = array_filter($files, function ($file): bool { return !($file['excludeInTest'] ?? false); }); }templates/unity/Assets/Samples~/AppwriteExample/AppwriteExample.cs.twig (1)
17-19
: Fix formatting: Remove extra blank lineThere's an unnecessary blank line between the method signature and opening brace.
private async void Start() - {
templates/unity/Assets/Runtime/Utilities/AppwriteUtilities.cs.twig (1)
35-36
: Remove unused variable assignmentLine 36 assigns
manager.Realtime
to variablea
which is never used. This appears to be leftover debug/test code.- //Create Realtime instance - var a =manager.Realtime;templates/unity/Assets/Runtime/Realtime.cs.twig (1)
358-383
: Async void pattern in UniTask.CreateUsing
UniTask.Create
with an async lambda creates a fire-and-forget task. Consider storing the task reference for proper lifecycle management.Consider storing the heartbeat task:
private UniTask _heartbeatTask; private void StartHeartbeat() { StopHeartbeat(); _heartbeatTokenSource = new CancellationTokenSource(); _heartbeatTask = UniTask.Create(async () => { // ... existing heartbeat logic ... }); }templates/unity/Assets/Editor/AppwriteSetupWindow.cs.twig (1)
250-251
: Potential race condition in delayed message clearingUsing
Task.Delay
withTaskScheduler.FromCurrentSynchronizationContext()
for clearing messages could fail if the synchronization context changes or becomes unavailable.Consider using Unity's coroutine system or EditorApplication callbacks:
private void ShowMessage(string message, MessageType type) { _statusMessage = message; _statusMessageType = type; Repaint(); if (type != MessageType.Error) { EditorApplication.delayCall += () => { EditorCoroutineUtility.StartCoroutine(ClearMessageAfterDelay(message, 5f), this); }; } } private System.Collections.IEnumerator ClearMessageAfterDelay(string message, float delay) { yield return new EditorWaitForSeconds(delay); if (_statusMessage == message) { _statusMessage = ""; Repaint(); } }templates/unity/Assets/Runtime/AppwriteManager.cs.twig (1)
136-137
: Hardcoded assumption about service namespaceThe code assumes all services are in the same namespace as the
Account
class. This assumption should be documented or made more flexible.Consider adding a comment to document this assumption:
+ // IMPORTANT: This assumes all service classes are in the same namespace as Account. + // If services are moved to different namespaces, this logic will need to be updated. var serviceNamespace = typeof(Account).Namespace; // Assumes all services are in the same namespace.templates/unity/Assets/Runtime/AppwriteConfig.cs.twig (1)
38-41
: Consider HTTPS default for cloud endpoints.Using HTTPS by default aligns with security best practices. The realtime endpoint correctly uses
wss://
for secure WebSocket connections.- [SerializeField] private string endpoint = "https://cloud.{{ spec.title | lower }}.io/v1"; + [SerializeField] private string endpoint = "https://cloud.{{ spec.title | lower }}.io/v1"; [Tooltip("WebSocket endpoint for realtime updates (optional)")] [SerializeField] private string realtimeEndpoint = "wss://cloud.{{ spec.title | lower }}.io/v1";templates/unity/Assets/Editor/AppwriteSetupAssistant.cs.twig (2)
144-152
: Consider adding timeout handling for package installation.While the current implementation is functional, long-running package installations could benefit from timeout handling to prevent indefinite waiting.
Consider adding a timeout mechanism:
private static void InstallPackage(string packageUrl, Action onCompleted, float timeout = 60f) { if (_isBusy) return; var queue = new Queue<string>(); queue.Enqueue(packageUrl); var startTime = EditorApplication.timeSinceStartup; InstallNextPackage(queue, () => onCompleted?.Invoke(), (error) => { if (EditorApplication.timeSinceStartup - startTime > timeout) { Debug.LogError($"Package installation timed out after {timeout} seconds"); } Debug.LogError(error); }); }
13-16
: Verified — package URLs are valid and actively maintained.
Both UniTask and NativeWebSocket UPM Git URLs are documented in their upstream READMEs and show recent activity; no immediate change required. Optional: document a fallback (pinned version or alternate registry) for resilience.templates/unity/Assets/Runtime/Core/Client.cs.twig (1)
725-731
: Consider limiting certificate acceptance scope.While accepting all certificates works for self-signed scenarios, consider validating against a specific certificate fingerprint or CA for better security.
public class AcceptAllCertificatesSignedWithASpecificKeyPublicKey : CertificateHandler { + // Consider adding constructor to accept specific certificate fingerprint + private readonly byte[] _allowedFingerprint; + + public AcceptAllCertificatesSignedWithASpecificKeyPublicKey(byte[] allowedFingerprint = null) + { + _allowedFingerprint = allowedFingerprint; + } + protected override bool ValidateCertificate(byte[] certificateData) { - return true; // Accept all certificates + if (_allowedFingerprint == null) return true; // Accept all if no fingerprint specified + + // Validate against specific fingerprint for better security + // Implementation would compute hash of certificateData and compare + return true; // Simplified for now } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (27)
templates/unity/Assets/Runtime/Core/Plugins/Microsoft.Bcl.AsyncInterfaces.dll
is excluded by!**/*.dll
templates/unity/Assets/Runtime/Core/Plugins/System.IO.Pipelines.dll
is excluded by!**/*.dll
templates/unity/Assets/Runtime/Core/Plugins/System.Runtime.CompilerServices.Unsafe.dll
is excluded by!**/*.dll
templates/unity/Assets/Runtime/Core/Plugins/System.Text.Encodings.Web.dll
is excluded by!**/*.dll
templates/unity/Assets/Runtime/Core/Plugins/System.Text.Json.dll
is excluded by!**/*.dll
templates/unity/ProjectSettings/AudioManager.asset
is excluded by!**/*.asset
templates/unity/ProjectSettings/ClusterInputManager.asset
is excluded by!**/*.asset
templates/unity/ProjectSettings/DynamicsManager.asset
is excluded by!**/*.asset
templates/unity/ProjectSettings/EditorBuildSettings.asset
is excluded by!**/*.asset
templates/unity/ProjectSettings/EditorSettings.asset
is excluded by!**/*.asset
templates/unity/ProjectSettings/GraphicsSettings.asset
is excluded by!**/*.asset
templates/unity/ProjectSettings/InputManager.asset
is excluded by!**/*.asset
templates/unity/ProjectSettings/MemorySettings.asset
is excluded by!**/*.asset
templates/unity/ProjectSettings/NavMeshAreas.asset
is excluded by!**/*.asset
templates/unity/ProjectSettings/NetworkManager.asset
is excluded by!**/*.asset
templates/unity/ProjectSettings/PackageManagerSettings.asset
is excluded by!**/*.asset
templates/unity/ProjectSettings/Physics2DSettings.asset
is excluded by!**/*.asset
templates/unity/ProjectSettings/PresetManager.asset
is excluded by!**/*.asset
templates/unity/ProjectSettings/ProjectSettings.asset
is excluded by!**/*.asset
templates/unity/ProjectSettings/QualitySettings.asset
is excluded by!**/*.asset
templates/unity/ProjectSettings/TagManager.asset
is excluded by!**/*.asset
templates/unity/ProjectSettings/TimeManager.asset
is excluded by!**/*.asset
templates/unity/ProjectSettings/UnityConnectSettings.asset
is excluded by!**/*.asset
templates/unity/ProjectSettings/VFXManager.asset
is excluded by!**/*.asset
templates/unity/ProjectSettings/VersionControlSettings.asset
is excluded by!**/*.asset
templates/unity/ProjectSettings/XRSettings.asset
is excluded by!**/*.asset
templates/unity/icon.png
is excluded by!**/*.png
📒 Files selected for processing (32)
.github/workflows/tests.yml
(2 hunks)example.php
(2 hunks)src/SDK/Language/Unity.php
(1 hunks)templates/unity/Assets/Editor/Appwrite.Editor.asmdef.twig
(1 hunks)templates/unity/Assets/Editor/AppwriteSetupAssistant.cs.twig
(1 hunks)templates/unity/Assets/Editor/AppwriteSetupWindow.cs.twig
(1 hunks)templates/unity/Assets/Runtime/Appwrite.asmdef.twig
(1 hunks)templates/unity/Assets/Runtime/AppwriteConfig.cs.twig
(1 hunks)templates/unity/Assets/Runtime/AppwriteManager.cs.twig
(1 hunks)templates/unity/Assets/Runtime/Core/Appwrite.Core.asmdef.twig
(1 hunks)templates/unity/Assets/Runtime/Core/Client.cs.twig
(1 hunks)templates/unity/Assets/Runtime/Core/CookieContainer.cs.twig
(1 hunks)templates/unity/Assets/Runtime/Core/Plugins/Android/AndroidManifest.xml
(1 hunks)templates/unity/Assets/Runtime/Core/Plugins/WebGLCookies.jslib
(1 hunks)templates/unity/Assets/Runtime/Core/Services/ServiceTemplate.cs.twig
(1 hunks)templates/unity/Assets/Runtime/Core/WebAuthComponent.cs.twig
(1 hunks)templates/unity/Assets/Runtime/Core/csc.rsp
(1 hunks)templates/unity/Assets/Runtime/Realtime.cs.twig
(1 hunks)templates/unity/Assets/Runtime/Utilities/AppwriteUtilities.cs.twig
(1 hunks)templates/unity/Assets/Samples~/AppwriteExample/AppwriteExample.cs.twig
(1 hunks)templates/unity/CHANGELOG.md.twig
(1 hunks)templates/unity/LICENSE.twig
(1 hunks)templates/unity/Packages/manifest.json
(1 hunks)templates/unity/Packages/packages-lock.json
(1 hunks)templates/unity/ProjectSettings/ProjectVersion.txt
(1 hunks)templates/unity/README.md.twig
(1 hunks)templates/unity/base/requests/oauth.twig
(1 hunks)templates/unity/docs/example.md.twig
(1 hunks)templates/unity/package.json.twig
(1 hunks)tests/Unity2021Test.php
(1 hunks)tests/languages/unity/Tests.asmdef
(1 hunks)tests/languages/unity/Tests.cs
(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
tests/Unity2021Test.php (2)
tests/languages/unity/Tests.cs (1)
Tests
(16-261)tests/Base.php (1)
Base
(17-275)
src/SDK/Language/Unity.php (3)
src/SDK/SDK.php (1)
SDK
(21-933)src/SDK/Language.php (1)
Language
(5-140)src/SDK/Language/DotNet.php (1)
DotNet
(8-457)
example.php (2)
src/SDK/SDK.php (16)
SDK
(21-933)setName
(254-259)setDescription
(265-270)setShortDescription
(276-281)setURL
(386-391)setLogo
(375-380)setLicenseContent
(320-325)setWarning
(441-446)setChangelog
(474-479)setVersion
(287-292)setGitUserName
(353-358)setGitRepoName
(342-347)setTwitter
(509-514)setDiscord
(497-503)setDefaultHeaders
(232-237)generate
(615-732)src/SDK/Language/Unity.php (1)
Unity
(5-409)
🔇 Additional comments (25)
templates/unity/package.json.twig (1)
18-24
: Samples entry looks good.Path and metadata are aligned with Unity packaging conventions.
templates/unity/README.md.twig (1)
110-112
: No change required — generated client exposes SetEndPointRealtime.
Found client.SetEndPointRealtime("wss://cloud.appwrite.io/v1") in tests/languages/unity/Tests.cs:49.templates/unity/CHANGELOG.md.twig (1)
1-1
: LGTM.Simple passthrough of sdk.changelog. No issues.
example.php (2)
25-25
: Unity language import — LGTMImport is correct and consistent with other languages.
80-104
: Unity example generation block — LGTMParameters mirror other examples and are sane for local runs.
tests/languages/unity/Tests.cs (1)
49-50
: Verify realtime endpoint setter nameMethod is
SetEndPointRealtime
here but the client usesSetEndpoint(...)
elsewhere. Confirm the exact API to avoid a compile error (typo in “EndPoint” vs “Endpoint”?).tests/Unity2021Test.php (1)
22-28
: UNITY_TEST_MODE toggle — LGTMThis keeps generation minimal for CI and aligns with Unity language provider behavior.
templates/unity/base/requests/oauth.twig (1)
7-7
: Confirm Authenticate() return typeCode expects a Uri (uses callbackUri.Query). Verify WebAuthComponent.Authenticate(authUrl) returns Uri, or wrap with new Uri(string).
tests/languages/unity/Tests.asmdef (1)
1-23
: LGTM! Assembly definition is properly configuredThe assembly definition correctly references all necessary dependencies including test runners, UniTask, and the main Appwrite assemblies. The configuration with
overrideReferences: true
and explicit NUnit reference is appropriate for Unity test assemblies.src/SDK/Language/Unity.php (3)
5-13
: LGTM! Proper inheritance from DotNetThe Unity language class correctly extends DotNet, which makes sense given Unity's C# foundation. This inheritance allows code reuse while maintaining Unity-specific customizations.
20-33
: Unity-specific keywords are appropriateThe keywords list correctly includes Unity-specific reserved words that should be escaped in generated code. The deduplication with
array_unique
ensures no duplicates from the parent class.
116-117
: Good practice: Reusing DotNet templates where applicableThe file correctly reuses many DotNet templates for shared functionality (Exception, ID, Permission, Query, Role, converters, etc.), which promotes code reuse and maintainability as suggested in past reviews.
Also applies to: 121-122, 125-127, 130-132, 135-137, 145-147, 149-152, 155-157, 160-162, 165-167, 170-172, 175-177, 185-187, 190-192, 200-202
templates/unity/Assets/Runtime/Core/Services/ServiceTemplate.cs.twig (1)
70-76
: Platform-specific stubs are well implementedGood use of conditional compilation to provide appropriate fallbacks for unsupported platforms, with clear warning messages for developers.
templates/unity/Assets/Samples~/AppwriteExample/AppwriteExample.cs.twig (1)
60-61
: Good practice: Service examples are commented outThe commented-out service creation examples provide helpful guidance without generating non-compilable code. This is a good approach for template-based examples.
Also applies to: 106-107
templates/unity/Assets/Runtime/AppwriteConfig.cs.twig (3)
55-56
: Security warning is appropriate for dev keys.Good security practice to warn about storing sensitive keys in ScriptableObjects. The warning message properly alerts developers to the security risk.
103-129
: Well-structured editor utility for configuration creation.The CreateConfiguration method properly handles directory creation, unique asset naming, and provides good user feedback. The use of the Resources folder ensures runtime loading compatibility.
11-14
: Fix incorrect bit masking for theMain
andOthers
service flags.The current bit mask calculations are incorrect.
Main
should cover bits 0-3 (Account, Databases, Functions, Storage), andOthers
should cover bits 4-8 (Avatars, Graphql, Locale, Messaging, Teams).Apply this fix:
- Main = (1 << 4) - 1, // 0-3 + Main = Account | Databases | Functions | Storage, // bits 0-3 [Tooltip("Selects all other services: Avatars, GraphQL, Locale, Messaging, Teams")] - Others = (1 << 9) - 1 ^ (1 << 4) - 1, // 4-8 + Others = Avatars | Graphql | Locale | Messaging | Teams, // bits 4-8Likely an incorrect or invalid review comment.
templates/unity/Assets/Editor/AppwriteSetupAssistant.cs.twig (2)
30-48
: Robust initialization flow with proper state management.The initialization check properly handles editor states (compiling, updating) and uses EditorPrefs for persistent state management. The conditional display of the setup window based on package availability is well-implemented.
86-104
: Excellent queue-based package installation implementation.The InstallAllPackages method properly handles concurrent operations, uses AssetDatabase.StartAssetEditing/StopAssetEditing for performance, and implements a clean recursive installation pattern with proper error handling.
templates/unity/Assets/Runtime/Core/CookieContainer.cs.twig (3)
30-33
: Good implementation of RFC 6265 cookie expiration logic.The IsExpired property correctly prioritizes Max-Age over Expires as per RFC 6265 specification.
73-86
: Good platform-specific initialization.The WebGL-specific credential enabling and conditional cookie loading based on platform is well-implemented.
145-149
: Cookie value parsing handles edge cases correctly.Using Split with count parameter of 2 properly handles cookie values containing '=' characters.
templates/unity/Assets/Runtime/Core/Client.cs.twig (3)
100-102
: Good URL validation for endpoint setting.The validation ensures endpoints start with proper protocol schemes.
259-365
: Comprehensive request preparation with proper multipart handling.The PrepareRequest method properly handles GET query strings, multipart form data with file uploads, JSON bodies, headers, cookies, and self-signed certificates. The implementation correctly processes InputFile from various sources (path, stream, bytes).
540-719
: Excellent chunked upload implementation.The ChunkedUpload method properly:
- Handles resume functionality by checking existing upload progress
- Supports multiple input sources (path, stream, bytes)
- Implements proper Content-Range headers
- Provides progress callbacks
- Handles both small files (single upload) and large files (chunked)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
example.php (1)
98-101
: Sync response-format header with selected spec version.
Hardcoding '1.6.0' risks drift from $version ('1.8.x' above). Derive '1.8.0' from $version to keep this example accurate.Apply this diff inside the Unity block:
- ->setDefaultHeaders([ - 'X-Appwrite-Response-Format' => '1.6.0', - ]) + ->setDefaultHeaders([ + 'X-Appwrite-Response-Format' => $responseFormat, + ])And add this once after the $version assignment (outside this hunk):
// Derive concrete response format (e.g., '1.8.x' -> '1.8.0') $responseFormat = preg_replace('/\.x$/', '.0', $version);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
example.php
(2 hunks)
🔇 Additional comments (1)
example.php (1)
25-25
: Unity provider import — LGTM.
Replaces 'async void' methods with non-async methods that call async methods using '.Forget()' to improve error handling and code clarity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (5)
templates/unity/base/requests/oauth.twig (2)
9-12
: Replace System.Web.HttpUtility; not available on Unity’s .NET Standard
HttpUtility.ParseQueryString
will not compile on Unity 2021/2022/6. Use a small parser instead.Apply:
- var query = HttpUtility.ParseQueryString(callbackUri.Query); - var secret = query.Get("secret"); - var key = query.Get("key"); - var callbackDomain = query.Get("domain"); // Get domain from callback + var pairs = callbackUri.Query.TrimStart('?').Split('&', StringSplitOptions.RemoveEmptyEntries); + var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); + foreach (var p in pairs) + { + var kv = p.Split('=', 2); + var k = Uri.UnescapeDataString(kv[0]); + var v = kv.Length > 1 ? Uri.UnescapeDataString(kv[1]) : string.Empty; + dict[k] = v; + } + dict.TryGetValue("secret", out var secret); + dict.TryGetValue("key", out var key); + dict.TryGetValue("domain", out var callbackDomain); // optional overrideNote: ensure
using System; using System.Collections.Generic;
are present.
25-31
: Gate cookie write in Editor to match the warningCurrently cookies are parsed even in Editor, contradicting the warning. Skip cookie creation in Editor.
Apply:
- var setCookieHeader = $"{key}={secret}; Path=/; Domain={parsedDomain}; Secure; HttpOnly; Max-Age={30 * 24 * 60 * 60}"; - Debug.Log($"Setting session cookie for domain: {parsedDomain}"); - _client.CookieContainer.ParseSetCookieHeader(setCookieHeader, parsedDomain); - -#if UNITY_EDITOR - Debug.LogWarning("[Appwrite] OAuth authorization in Editor: you can open and authorize, but cookies cannot be obtained. The session will not be set."); -#endif + #if UNITY_EDITOR + Debug.LogWarning("[Appwrite] OAuth in Editor: cookies are not persisted; session will not be set."); + #else + var setCookieHeader = $"{key}={secret}; Path=/; Domain={parsedDomain}; Secure; HttpOnly; Max-Age={30 * 24 * 60 * 60}"; + Debug.Log($"Setting session cookie for domain: {parsedDomain}"); + _client.CookieContainer.ParseSetCookieHeader(setCookieHeader, parsedDomain); + #endiftemplates/unity/Assets/Runtime/Realtime.cs.twig (3)
292-307
: Null-guard_client
/Config
in fallback authAvoid potential NREs if
Initialize
hasn’t run orConfig
is null.Apply:
private void SendFallbackAuthentication() { - var session = _client.Config.GetValueOrDefault("session"); + if (_client?.Config == null) + { + Debug.LogError("[Realtime] Cannot send fallback authentication: client/config is null"); + return; + } + var session = _client.Config.GetValueOrDefault("session");
463-466
: Avoid async cleanup in OnDestroy; perform best-effort synchronous teardownUnity doesn’t await async work on destroy. Stop heartbeat/reconnect and close the socket immediately.
Apply:
- private void OnDestroy() - { - Disconnect().Forget(); - } + private void OnDestroy() + { + _reconnect = false; + StopHeartbeat(); + _cancellationTokenSource?.Cancel(); + if (_webSocket != null && _webSocket.State != WebSocketState.Closed) + { + // fire-and-forget close; Unity won't await here + _webSocket.Close(); + } + }
126-152
: Channels added during in-flight connect can be missed; also reset reconnect flagIf
Subscribe
is called while a connection is being established, the secondCreateSocket()
returns early due to_creatingSocket
, and the new channels are not included after the first connect completes. Also, after a previousCloseConnection()
,_reconnect
stays false; set it before connecting.Apply:
var subscription = new RealtimeSubscription { Channels = channels, OnMessage = callback, OnClose = () => CloseSubscription(subscriptionId, channels) }; @@ - CreateSocket().Forget(); + _reconnect = true; + if (_creatingSocket) + { + UniTask.Create(async () => + { + await UniTask.WaitUntil(() => !_creatingSocket); + await CreateSocket(); + }); + } + else + { + UniTask.Create(async () => { await CreateSocket(); }); + }
🧹 Nitpick comments (1)
templates/unity/Assets/Runtime/Realtime.cs.twig (1)
100-101
: Unused cancellation token source
_cancellationTokenSource
is declared and cancelled but never created or used to drive operations. Either wire it into connect/heartbeat flows or remove it.Also applies to: 448-456
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
templates/unity/Assets/Runtime/AppwriteManager.cs.twig
(1 hunks)templates/unity/Assets/Runtime/Realtime.cs.twig
(1 hunks)templates/unity/Assets/Runtime/Utilities/AppwriteUtilities.cs.twig
(1 hunks)templates/unity/Assets/Samples~/AppwriteExample/AppwriteExample.cs.twig
(1 hunks)templates/unity/base/requests/oauth.twig
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- templates/unity/Assets/Samples~/AppwriteExample/AppwriteExample.cs.twig
- templates/unity/Assets/Runtime/Utilities/AppwriteUtilities.cs.twig
- templates/unity/Assets/Runtime/AppwriteManager.cs.twig
#if !UNITY_WEBGL || UNITY_EDITOR | ||
_webSocket?.DispatchMessageQueue(); | ||
#endif | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WebGL message pump is inverted; events won’t dispatch on WebGL
DispatchMessageQueue
must run on WebGL builds (not in Editor). Current guard prevents it.
Apply:
- #if !UNITY_WEBGL || UNITY_EDITOR
- _webSocket?.DispatchMessageQueue();
- #endif
+ #if UNITY_WEBGL && !UNITY_EDITOR
+ _webSocket?.DispatchMessageQueue();
+ #endif
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
#if !UNITY_WEBGL || UNITY_EDITOR | |
_webSocket?.DispatchMessageQueue(); | |
#endif | |
} | |
#if UNITY_WEBGL && !UNITY_EDITOR | |
_webSocket?.DispatchMessageQueue(); | |
#endif | |
} |
🤖 Prompt for AI Agents
In templates/unity/Assets/Runtime/Realtime.cs.twig around lines 121 to 124, the
preprocessor guard is inverted so DispatchMessageQueue is skipped on WebGL
builds; change the conditional to run DispatchMessageQueue on WebGL builds but
not in the Editor by replacing the current #if !UNITY_WEBGL || UNITY_EDITOR
check with the correct guard (run when UNITY_WEBGL is defined and UNITY_EDITOR
is not) so _webSocket?.DispatchMessageQueue() executes on WebGL runtime builds
only.
private void HandleRealtimeEvent(RealtimeResponseEvent<Dictionary<string, object>> eventData) | ||
{ | ||
try | ||
{ | ||
var subscriptionsCopy = _subscriptions.Values.ToArray(); | ||
foreach (var subscription in subscriptionsCopy) | ||
{ | ||
if (subscription.Channels.Any(subChannel => eventData.Channels.Contains(subChannel))) | ||
{ | ||
subscription.OnMessage?.Invoke(eventData); | ||
} | ||
} | ||
} | ||
catch (Exception ex) | ||
{ | ||
Debug.LogError($"[Realtime] HandleRealtimeEvent error: {ex.Message}"); | ||
OnError?.Invoke(ex); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Ensure user callbacks run on Unity main thread
On non-WebGL platforms, NativeWebSocket
invokes OnMessage
on a background thread. Invoking user callbacks there can break Unity APIs and cause data races. Post to main thread.
Apply:
- var subscriptionsCopy = _subscriptions.Values.ToArray();
- foreach (var subscription in subscriptionsCopy)
- {
- if (subscription.Channels.Any(subChannel => eventData.Channels.Contains(subChannel)))
- {
- subscription.OnMessage?.Invoke(eventData);
- }
- }
+ var subscriptionsCopy = _subscriptions.Values.ToArray();
+ UniTask.Post(() =>
+ {
+ foreach (var subscription in subscriptionsCopy)
+ {
+ if (subscription.Channels.Any(subChannel => eventData.Channels.Contains(subChannel)))
+ {
+ subscription.OnMessage?.Invoke(eventData);
+ }
+ }
+ });
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
private void HandleRealtimeEvent(RealtimeResponseEvent<Dictionary<string, object>> eventData) | |
{ | |
try | |
{ | |
var subscriptionsCopy = _subscriptions.Values.ToArray(); | |
foreach (var subscription in subscriptionsCopy) | |
{ | |
if (subscription.Channels.Any(subChannel => eventData.Channels.Contains(subChannel))) | |
{ | |
subscription.OnMessage?.Invoke(eventData); | |
} | |
} | |
} | |
catch (Exception ex) | |
{ | |
Debug.LogError($"[Realtime] HandleRealtimeEvent error: {ex.Message}"); | |
OnError?.Invoke(ex); | |
} | |
} | |
private void HandleRealtimeEvent(RealtimeResponseEvent<Dictionary<string, object>> eventData) | |
{ | |
try | |
{ | |
var subscriptionsCopy = _subscriptions.Values.ToArray(); | |
UniTask.Post(() => | |
{ | |
foreach (var subscription in subscriptionsCopy) | |
{ | |
if (subscription.Channels.Any(subChannel => eventData.Channels.Contains(subChannel))) | |
{ | |
subscription.OnMessage?.Invoke(eventData); | |
} | |
} | |
}); | |
} | |
catch (Exception ex) | |
{ | |
Debug.LogError($"[Realtime] HandleRealtimeEvent error: {ex.Message}"); | |
OnError?.Invoke(ex); | |
} | |
} |
🤖 Prompt for AI Agents
In templates/unity/Assets/Runtime/Realtime.cs.twig around lines 314-332, user
callbacks (subscription.OnMessage) are currently invoked on the NativeWebSocket
background thread; marshal those invocations to the Unity main thread instead.
Modify the loop so that for each matching subscription you enqueue a delegate to
the main thread (using your project’s main-thread executor—e.g., a
UnityMainThreadDispatcher, a captured Unity SynchronizationContext, or
Application.InvokeOnAppThread) that calls
subscription.OnMessage?.Invoke(eventData); ensure you capture
eventData/subscription locally before enqueuing and leave WebGL behavior
unchanged if necessary.
// Use domain from callback if available, otherwise fallback to endpoint host | ||
var domain = !string.IsNullOrEmpty(callbackDomain) ? callbackDomain : new Uri(_client.Endpoint).Host; | ||
var parsedDomain = domain.StartsWith(".") ? domain.Substring(1) : domain; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Validate callbackDomain before using it to set cookie domain
Trusting a query param for cookie scope is risky. Restrict to the endpoint host or its subdomains; otherwise, fall back to the endpoint host.
Apply:
- var domain = !string.IsNullOrEmpty(callbackDomain) ? callbackDomain : new Uri(_client.Endpoint).Host;
+ var endpointHost = new Uri(_client.Endpoint).Host;
+ var domain =
+ !string.IsNullOrEmpty(callbackDomain) &&
+ (string.Equals(callbackDomain, endpointHost, StringComparison.OrdinalIgnoreCase) ||
+ callbackDomain.EndsWith("." + endpointHost, StringComparison.OrdinalIgnoreCase))
+ ? callbackDomain
+ : endpointHost;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
// Use domain from callback if available, otherwise fallback to endpoint host | |
var domain = !string.IsNullOrEmpty(callbackDomain) ? callbackDomain : new Uri(_client.Endpoint).Host; | |
var parsedDomain = domain.StartsWith(".") ? domain.Substring(1) : domain; | |
var endpointHost = new Uri(_client.Endpoint).Host; | |
var domain = | |
!string.IsNullOrEmpty(callbackDomain) && | |
(string.Equals(callbackDomain, endpointHost, StringComparison.OrdinalIgnoreCase) || | |
callbackDomain.EndsWith("." + endpointHost, StringComparison.OrdinalIgnoreCase)) | |
? callbackDomain | |
: endpointHost; | |
var parsedDomain = domain.StartsWith(".") ? domain.Substring(1) : domain; |
🤖 Prompt for AI Agents
In templates/unity/base/requests/oauth.twig around lines 20-22, the code uses
callbackDomain directly for cookie scope which is unsafe; instead, parse and
validate callbackDomain against the endpoint host and only accept it if it
equals the endpoint host or is a direct subdomain (e.g., callbackHost ==
endpointHost or callbackHost.EndsWith("." + endpointHost)); strip any leading
dot, otherwise fall back to endpoint host; implement: extract host from
_client.Endpoint, extract/normalize host from callbackDomain (remove scheme/port
and leading dot), perform the equality/subdomain check, and only then set domain
to callbackHost, else set domain to endpointHost.
What does this PR do?
This PR adds comprehensive Unity SDK support to the SDK generator by:
Unity Language Implementation: Introduces a new
Unity
language class (src/SDK/Language/Unity.php
) that extends the baseLanguage
class, providing Unity-specific type mappings, keywords, and code generation logic for C# in Unity environment.Unity Template System: Adds a complete set of Unity-specific templates under
templates/unity/Assets/
including:Automated Testing Integration: Introduces Unity2021 test support with:
Unity2021Test.php
test class that integrates with the existing test frameworkTests.cs
,Tests.asmdef
) for comprehensive SDK testingunityci/editor:ubuntu-2021.3.45f1-base-3.1.0
CI/CD Integration: Updates the GitHub Actions workflow to:
UNITY_LICENSE
environment variable for Unity Editor automationUnity Project Structure: Implements proper Unity package structure with:
Assets/Runtime/
andAssets/Editor/
Test Plan
The testing strategy includes:
Unit Testing: The
Unity2021Test.php
runs comprehensive tests covering:Docker-based CI Testing: Tests run in a controlled Unity environment using:
Related PRs and Issues
(If this PR is related to any other PR or resolves any issue or related to any issue link all related PR and issues here.)
Have you read the Contributing Guidelines on issues?
Yes
Summary by CodeRabbit
New Features
Documentation
Tests
Chores